/*
    Copyright 2011 Val Rosca

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var JSIndex = {
    DELIM: /\s+/,
    parseDOM: function(element, childNodeTagName, textExtractionFunction) {
        var tokenToElementsHash = {};
        var elementIndex = 0;

        var descendants = element.getElementsByTagName(childNodeTagName);
        var descendantCount = descendants.length;
        var descendant;
        var customTextExtraction = (typeof textExtractionFunction == "function");

        var ieTextExtraction = $.browser.msie;

        var text, splitText, splitTextLength, h;

        for (var i=0; i<descendantCount; i++) {
            descendant = descendants[i];
            if (customTextExtraction) {
                text = textExtractionFunction(descendant);
            } else if (ieTextExtraction) {
                text = descendant.innerText;
            } else {
                text = descendant.textContent;
            }
            if (text) {
                splitText = text.toLowerCase().split(this.DELIM);
                splitTextLength = splitText.length;
                elementIndex++;

                for (var j=0; j<splitTextLength; j++) {
                    if (h = tokenToElementsHash[splitText[j]]) {
                        h[elementIndex] = descendant;
                    } else {
                        (tokenToElementsHash[splitText[j]] = {})[elementIndex] =  descendant;
                    }
                }
            }
        }

        var tokens = [];
        for (var token in tokenToElementsHash)
            tokens.push(token);

        return this.create(tokens, tokenToElementsHash, descendants);
    },
    create: function(tokens, tokenToElementsHash, descendants) {
        tokens.sort();
        var parent = this;
        return {
            tokens: tokens,
            tokenToElementsHash: tokenToElementsHash,
            descendants: descendants,
            search: function(expression) {
                expression = expression.toLowerCase();
                var searchTokens = expression.split(parent.DELIM);
                var token;
                var tokenCount = tokens.length;
                var matchedElementsHash = {};
                var i;
                var addToMatched = function(hash) {
                    for(var e in hash)
                        matchedElementsHash[e] = hash[e];
                };
                for (i in searchTokens) {
                    token = searchTokens[i];
                    var lo = 0, hi = tokenCount - 1, m = -1, mv;
                    while(lo <= hi) {
                        m = lo + hi;
                        if (m % 2 == 1)
                            m--;
                        m /= 2;
                        mv = tokens[m];
                        if (token > mv) {
                            lo = m + 1;
                        } else {
                            hi = m - 1;
                        }
                    }
                    var matchIndex = -1;
                    for (var c = (m-1 >= 0 ? m - 1 : 0); c <= (m + 1 > tokenCount - 1 ? tokenCount - 1 : m + 1); c++) {
                        if(tokens[c].indexOf(token) == 0) {
                            matchIndex = c;
                            break;
                        }
                    }
                    if (matchIndex >= 0) {
                        var j, t;
                        for (j=matchIndex; j>=0 && (t=tokens[j]).indexOf(token) == 0; j--) {
                            addToMatched(tokenToElementsHash[t]);
                        }
                        for (j=matchIndex; j<tokenCount && (t=tokens[j]).indexOf(token) == 0; j++) {
                            addToMatched(tokenToElementsHash[t]);
                        }
                    }
                }
                var matchedElements = [];
                for (i in matchedElementsHash)
                    matchedElements.push(matchedElementsHash[i]);
                return matchedElements;
            }
        };
    }
};
